package samples

import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.yield
import org.junit.Test
import java.io.IOException

/**
 *
 * @author mph
 * @date 2025/1/16
 */
class ExceptionTest {

    @Test
    fun test2() {
        Thread.setDefaultUncaughtExceptionHandler { t, e ->
            println("Uncaught Exception: ${e.message}")
        }
//        throw RuntimeException("test")
        Thread {
            //如果加了try捕获到了则UncaughtExceptionHandler中就不会再抛出异常
//            try {
            throw RuntimeException("test")
//            }catch (e:Exception){
//                println("Done Exception: ${e.message}")
//            }
        }.start()
        Thread.sleep(3000)
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    @Test
    fun test3() = runTest {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("CoroutineExceptionHandler got $exception")
        }
        val job = GlobalScope.launch(handler) { // root coroutine, running in GlobalScope
            throw AssertionError()
        }
        val deferred = GlobalScope.async(handler) { // also root, but async instead of launch
            throw ArithmeticException() // Nothing will be printed, relying on user to call deferred.await()
        }
        joinAll(job, deferred)
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    @Test
    fun test4() = runTest {
        val job = launch {
            val child = launch {
                try {
                    delay(Long.MAX_VALUE)
                } finally {
                    println("Child is cancelled")
                }
            }
            yield() //让出CPU资源给其他线程
            println("Cancelling child")
            child.cancel()
            child.join()
            yield()
            println("Parent is not cancelled")
        }
        job.join()
    }

    /**
     * 当有多个异常抛出时，只有第一个异常会被捕获，后面的异常会被添加到 其suppressed 中
     */
    @OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class)
    @Test
    fun test5() = runTest {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("CoroutineExceptionHandler got $exception with suppressed ${exception.suppressed.contentToString()}")
        }
        val job = GlobalScope.launch(handler) {
            launch {
                try {
                    delay(Long.MAX_VALUE) // it gets cancelled when another sibling fails with IOException
                } finally {
                    throw ArithmeticException() // the second exception
                }
            }
            launch {
                delay(100)
                throw IOException() // the first exception
            }
            delay(Long.MAX_VALUE)
        }
        job.join()
    }

    /**
     * SupervisorJob可以取消其所有子协程
     */
    @OptIn(ExperimentalCoroutinesApi::class)
    @Test
    fun test1() = runTest {
        val supervisor = SupervisorJob()
        with(CoroutineScope(coroutineContext + supervisor)) {
            // launch the first child -- its exception is ignored for this example (don't do this in practice!)
            val firstChild = launch(CoroutineExceptionHandler { _, _ -> }) {
                println("The first child is failing")
                throw AssertionError("The first child is cancelled")
            }
            // launch the second child
            val secondChild = launch {
                firstChild.join()
                // Cancellation of the first child is not propagated to the second child
                println("The first child is cancelled: ${firstChild.isCancelled}, but the second one is still active")
                try {
                    delay(Long.MAX_VALUE)
                } finally {
                    // But cancellation of the supervisor is propagated
                    println("The second child is cancelled because the supervisor was cancelled")
                }
            }
            // wait until the first child fails & completes
            firstChild.join()
            println("Cancelling the supervisor")
            supervisor.cancel()
            secondChild.join()
        }
    }

    /**
     * 还可以用supervisorScope中抛出异常来取消所有的子协程
     */
    @OptIn(ExperimentalCoroutinesApi::class)
    @Test
    fun test6() = runTest {
        try {
            supervisorScope {
                val child = launch {
                    try {
                        println("The child is sleeping")
                        delay(Long.MAX_VALUE)
                    } finally {
                        println("The child is cancelled")
                    }
                }
                // Give our child a chance to execute and print using yield
                yield()
                println("Throwing an exception from the scope")
                throw AssertionError()
            }
        } catch (e: AssertionError) {
            println("Caught an assertion error")
        }
    }

    /**
     * supervisorScope意在必要时结束所有子线程，因此其子线程的异常不应该影响supervisorScope，但是按照一般的异常传递规则会影响到，因此子协程应该添加自己的CoroutineExceptionHandler来处理异常
     */
    @Test
    fun test7() = runTest {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("CoroutineExceptionHandler got $exception")
        }
        supervisorScope {
            val child = launch(handler) {
                println("The child throws an exception")
                throw AssertionError()
            }
            println("The scope is completing")
        }
        println("The scope is completed")
    }

}